#include "stdtype.h"
#include "overload.h"
#include "bit.h"

/// ------------------------------------------------------------------------ TCR (Translate Configuration Register) relevant

/* the TCR register bit mask */
#define MMU_TCR_T0SZ_MASK   RNGMSK(5, 0)        /* Translation virtual address bit size */
#define MMU_TCR_EPD0_MASK   BITMSK(7)
#define MMU_TCR_IRGN0_MASK  RNGMSK(9, 8)        /* Inner Cache Policy, for L1 cache */
#define MMU_TCR_ORGN0_MASK  RNGMSK(11, 10)      /* Outer Cache Policy, for L2/3 cache */
#define MMU_TCR_SH0_MASK    RNGMSK(13, 12)      /* Memory Shareable */
#define MMU_TCR_TG0_MASK    RNGMSK(15, 14)      /* Translation Granule Size */
#define MMU_TCR_T1SZ_MASK   RNGMSK(21, 16)      /* Translation virtual address bit size with TTBR1 */
#define MMU_TCR_A1_MASK     BITMSK(22)          /* ASID selects */
#define MMU_TCR_EPD1_MASK   BITMSK(23)
#define MMU_TCR_IRGN1_MASK  RNGMSK(25, 24)
#define MMU_TCR_ORGN1_MASK  RNGMSK(27, 26)
#define MMU_TCR_SH1_MASK    RNGMSK(29, 28)
#define MMU_TCR_TG1_MASK    RNGMSK(31, 30)
#define MMU_TCR_IPS_MASK    RNGMSK(34, 32)
#define MMU_TCR_AS_MASK     BITMSK(36)          /* ASID size */
#define MMU_TCR_TBI0_MASK   BITMSK(37)          /* Top Byte ignored */
#define MMU_TCR_TBI1_MASK   BITMSK(38)

/// ------------------------------------------------------------------------ MAIR (Memory Attribute Indirection Register) relevant

/**
 * the attribute of device memory
 * @prototype: mx_uint8_t MMU_MAIR_DEVICE (gn, gre);
 *      @param gn: group number of MAIR
 *      @param gre: __DEVICE_xxx
 * @note: about GRE, get more information from ARM document about GRE (chapter 'memory ordering')
 */
#define _DEVICE_nGnRnE      0                   /* 2b'00 Device-nGnRnE memory */
#define _DEVICE_nGnRE       1                   /* 2b'01 Device-nGnRE memory */
#define _DEVICE_nGRE        2                   /* 2b'10 Device-nGRE memory */
#define _DEVICE_GRE         3                   /* 2b'11 Device-GRE memory */

#define MMU_MAIR_DEVICE(gn, gre) ((gre << 2) << (gn * 8))                   /* the device memory attribute,
                                                                               8b'0000_dd00, dd is GRE */

/**
 * the attribute of normal memory
 * @prototype: mx_uint8_t MMU_MAIR_NORMAL(gn, tagged);
 *             mx_uint8_t MMU_MAIR_NORMAL(gn, outer, inner);
 *      @param gn: group number of MAIR
 *      @param tagged: a fixed value that '_NORMAL_TAGGED_NORMAL'
 *      @param outer: the attribute of L2/3-cache, 4 bits
 *      @param inner: the attribute of L1-cache, 4 bits
 *      @param ra: a cache allocation policy, read allocate
 *      @param wa: a cache allocation policy, write allocate
 * @example: MMU_MAIR_NORMAL(0, _NORMAL_WB_TRANSIENT(1, 1), _NORMAL_WB_TRANSIENT(0, 1));
 * @example: MMU_MAIR_NORMAL(1, _NORMAL_TAGGED_NORMAL);
 */
#define _NORMAL_NON_CACHEABLE 0x04                                          /* 4b'0100,               Non-cacheable */
#define _NORMAL_WT_TRANSIENT(ra, wa) ((0 << 2) | (ra << 1) | wa)            /* 4b'00RW, RW not 0b'00, Write-Through Transient */
#define _NORMAL_WT_NON_TRANSIENT(ra, wa) ((2 << 2) | (ra << 1) | wa)        /* 4b'10RW, RW not 0b'00, Write-Through Non-transient */
#define _NORMAL_WB_TRANSIENT(ra, wa) ((1 << 2) | (ra << 1) | wa)            /* 4b'01RW, RW not 0b'00, Write-Back    Transient */
#define _NORMAL_WB_NON_TRANSIENT(ra, wa) ((3 << 2) | (ra << 1) | wa)        /* 4b'11RW, RW not 0b'00, Write-Back    Non-transient */
#define _NORMAL_TAGGED_NORMAL 0xf0                                          /* 8b'11110000, Tagged Normal Memory. Inner+Outer Write-Back Non-transient memory, Inner+Outer Read-Allocate, Inner+Outer Write-Allocate */

#define __MMU_MAIR_NORMAL1(p0, p1, p2, p3, p4, p5, p6, p7) (p1 << (p0 * 8))
#define __MMU_MAIR_NORMAL2(p0, p1, p2, p3, p4, p5, p6, p7) (((p1 << 4) | p2) << (p0 * 8))
#define MMU_MAIR_NORMAL(...) OVERLOAD(__MMU_MAIR_NORMAL, __VA_ARGS__)        /* the normal memory attribute,
                                                                               8b'ooooiiii/8b'11110000, the parametric prototype are (gn, tagged) or (gn, outer, inner); bit number of parameters equals 8 */

/// ------------------------------------------------------------------------ the MMU table entry relevant

/**
 * the mmu table descriptor entry for stage 1
 * @prototype: mx_uint64_t MMU_TABLED_ENTRY_xxx (ns, ap, xn, pxn, ntaddr);   {xxx = 4K/16K/64K}
 *      @param ns: normal secure
 *      @param ap: access permission
 *      @param xn: execute never
 *      @param pxn: privilege execute never
 *      @param ntaddr: address of next level table descriptor
 *      @return: mmu table descritor entry
 */
#define _MMU_TABLED_ADDRALIGN_TG4K 12
#define _MMU_TABLED_ADDRALIGN_TG16K 14
#define _MMU_TABLED_ADDRALIGN_TG64K 16
#define _MMU_TABLED_ADDRALIGN_TG4K_MASK ((1UL << _MMU_TABLED_ADDRALIGN_TG4K) - 1)
#define _MMU_TABLED_ADDRALIGN_TG16K_MASK ((1UL << _MMU_TABLED_ADDRALIGN_TG16K) - 1)
#define _MMU_TABLED_ADDRALIGN_TG64K_MASK ((1UL << _MMU_TABLED_ADDRALIGN_TG64K) - 1)

#define _MMU_TABLED_ENTRY(alignmask, ns, ap, xn, pxn, ntaddr) (((mx_uint64_t)(ns) << 63) | ((mx_uint64_t)(ap) << 61) |    \
                                                               ((mx_uint64_t)(xn) << 60) | ((mx_uint64_t)(pxn) << 59)|    \
                                                               (ntaddr & ~alignmask) | 0x03)
#define MMU_TABLED_ENTRY_TG4K(ns, ap, xn, pxn, ntaddr) _MMU_TABLED_ENTRY(_MMU_TABLED_ADDRALIGN_TG4K_MASK, ns, ap, xn, pxn, ntaddr)
#define MMU_TABLED_ENTRY_TG16K(ns, ap, xn, pxn, ntaddr) _MMU_TABLED_ENTRY(_MMU_TABLED_ADDRALIGN_TG16K_MASK, ns, ap, xn, pxn, ntaddr)
#define MMU_TABLED_ENTRY_TG64K(ns, ap, xn, pxn, ntaddr) _MMU_TABLED_ENTRY(_MMU_TABLED_ADDRALIGN_TG64K_MASK, ns, ap, xn, pxn, ntaddr)

/**
 * the mmu block entry for level1/2 stage 1, entry of level0 can't be block
 * @prototype: mx_uint64_t MMU_BLOCK_ENTRY_xxx (rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr);
 *      @param rfs: reserved for software
 *      @param uxn: user execute never
 *      @param pxn: privilege execute never
 *      @param af: access flag
 *      @param sh: shareablility
 *      @param ap: access permission
 *      @param ns: normal secure
 *      @param idx: group index of MAIR which is control the memory attribute
 *      @param blkaddr: block address (start address of PA)
 *      @return: mmu block entry
 */
#define _MMU_BLOCK_ADDRALIGN_TG4K_L1 30
#define _MMU_BLOCK_ADDRALIGN_TG4K_L2 21
#define _MMU_BLOCK_ADDRALIGN_TG16K_L2 25
#define _MMU_BLOCK_ADDRALIGN_TG64K_L2 29
#define _MMU_BLOCK_ADDRALIGN_TG4K_L1_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG4K_L1) - 1)
#define _MMU_BLOCK_ADDRALIGN_TG4K_L2_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG4K_L2) - 1)
#define _MMU_BLOCK_ADDRALIGN_TG16K_L2_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG16K_L2) - 1)
#define _MMU_BLOCK_ADDRALIGN_TG64K_L2_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG64K_L2) - 1)

#define _MMU_BLOCK12_ENTRY(alignmask, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) ((rfs << 55) | (uxn << 54) | (pxn << 53) | (blkaddr & ~alignmask) |    \
                                                                                    (af << 10) | (sh << 8) | (ap << 6) | (ns << 5) | (idx << 2) | 0x01)
#define MMU_BLOCK_ENTRY_TG4K_L1(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK12_ENTRY(_MMU_BLOCK_ADDRALIGN_TG4K_L1_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)
#define MMU_BLOCK_ENTRY_TG4K_L2(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK12_ENTRY(_MMU_BLOCK_ADDRALIGN_TG4K_L2_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)
#define MMU_BLOCK_ENTRY_TG16K_L2(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK12_ENTRY(_MMU_BLOCK_ADDRALIGN_TG16K_L2_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)
#define MMU_BLOCK_ENTRY_TG64K_L2(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK12_ENTRY(_MMU_BLOCK_ADDRALIGN_TG64K_L2_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)

/**
 * the mmu block entry for level3 stage 1
 * @prototype: mx_uint64_t MMU_BLOCK_ENTRY_xxx (rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr);
 *      @param rfs: reserved for software
 *      @param uxn: user execute never
 *      @param pxn: privilege execute never
 *      @param af: access flag
 *      @param sh: shareablility
 *      @param ap: access permission
 *      @param ns: normal secure
 *      @param idx: group index of MAIR which is control the memory attribute
 *      @param blkaddr: block address (start address of PA)
 *      @return: mmu block entry
 */
#define _MMU_BLOCK_ADDRALIGN_TG4K_L3 12
#define _MMU_BLOCK_ADDRALIGN_TG16K_L3 14
#define _MMU_BLOCK_ADDRALIGN_TG64K_L3 16
#define _MMU_BLOCK_ADDRALIGN_TG4K_L3_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG4K_L3) - 1)
#define _MMU_BLOCK_ADDRALIGN_TG16K_L3_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG16K_L3) - 1)
#define _MMU_BLOCK_ADDRALIGN_TG64K_L3_MASK ((1UL << _MMU_BLOCK_ADDRALIGN_TG64K_L3) - 1)

#define _MMU_BLOCK3_ENTRY(alignmask, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) (((mx_uint64_t)(rfs) << 55) | ((mx_uint64_t)(uxn) << 54) | ((mx_uint64_t)(pxn) << 53) | (blkaddr & ~alignmask) |    \
                                                                                   (af << 10) | (sh << 8) | (ap << 6) | (ns << 5) | (idx << 2) | 0x03)
#define MMU_BLOCK_ENTRY_TG4K_L3(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK3_ENTRY(_MMU_BLOCK_ADDRALIGN_TG4K_L3_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)
#define MMU_BLOCK_ENTRY_TG16K_L3(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK3_ENTRY(_MMU_BLOCK_ADDRALIGN_TG16K_L3_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)
#define MMU_BLOCK_ENTRY_TG64K_L3(rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr) _MMU_BLOCK3_ENTRY(_MMU_BLOCK_ADDRALIGN_TG64K_L3_MASK, rfs, uxn, pxn, af, sh, ap, ns, idx, blkaddr)

/* mmu table entry region mask */
// next-level-table address mask for levelx table descriptor mmu entry 
#define MMU_NTADDR_TG4K_MASK RNGMSK(47, _MMU_TABLED_ADDRALIGN_TG4K)
#define MMU_NTADDR_TG16K_MASK RNGMSK(47, _MMU_TABLED_ADDRALIGN_TG16K)
#define MMU_NTADDR_TG64K_MASK RNGMSK(47, _MMU_TABLED_ADDRALIGN_TG64K)
// output-address mask for level3 block mmu entry
#define MMU_BLKADDR_TG4K_L3_MASK RNGMSK(47, _MMU_BLOCK_TG4K_L3)
#define MMU_BLKADDR_TG16K_L3_MASK RNGMSK(47, _MMU_BLOCK_TG16K_L3)
#define MMU_BLKADDR_TG64K_L3_MASK RNGMSK(47, _MMU_BLOCK_TG64K_L3)
// valid-indication mask for mmu entry (bit[0])
#define MMU_TABLE_ENTRY_VALID_MASK BITMSK(0)

/* virtual address level mask */
// levelx offset mask for virtual address of granule size 4K
#define MMU_OFFSET_TG4K_L0_MASK RNGMSK(47, 39)
#define MMU_OFFSET_TG4K_L1_MASK RNGMSK(38, 30)
#define MMU_OFFSET_TG4K_L2_MASK RNGMSK(29, 21)
#define MMU_OFFSET_TG4K_L3_MASK RNGMSK(20, 12)
// levelx offset mask for virtual address of granule size 16K
// ......

/* common align */
#define MMU_ALIGN_4K 12
#define MMU_VALUE_4K (1UL << MMU_ALIGN_4K)
#define MMU_ALIGN_4K_MASK (MMU_VALUE_4K - 1)

/* map 4k (granule size is 4k) */
void mmu_map_4k (mx_uint64_pt table, mx_void_pt va, mx_void_pt pa, mx_bool_t isdev, mx_usize_t multi);
